#define _GNU_SOURCE

#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <endian.h>
#include <errno.h>
#include <scsi/scsi.h>

#include "scsi_defs.h"
#include "libtcmu.h"
#include "tcmu-runner.h"
#include "tcmur_device.h"

#define TCMU_KV_DEMO_DIR "/root/tcmu_kv_demo/"
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    key        [输入key]
 * @out    value     [得到的value]
 * @return ret       [返回读到的数据大小]
 * 这个函数是使用文件对KV存储读服务的仿真。这里用一个文件对应一个对象，文件名即键值。
 * 如果要换成mysql作为底层存储，请在这个函数中改动。
 */
int OBJ_read(char* key, char* value){
	char path[512];
	int fd;
	size_t file_len;
	sprintf(path, "%s%s", TCMU_KV_DEMO_DIR, key);
	fd = open(path, O_CREAT | O_RDONLY, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		tcmu_err("could not open %s: %m\n", path);
		return -1;
	}
	file_len = strlen(value);
	ssize_t ret = read(fd, value, file_len);
	close(fd);
	return ret;
}
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    key        [输入入key]
 * @in    value      [要写入的内容]
 * @return ret       [返回写入的数据大小]
 * 这个函数是使用文件对KV存储写服务的仿真。这里用一个文件对应一个对象，文件名即键值。
 * 如果要换成mysql作为底层存储，请在这个函数中改动。
 */
int OBJ_write(char* key, char* value){
	char path[512];
	int fd;
	size_t file_len;
	sprintf(path, "%s%s", TCMU_KV_DEMO_DIR, key);
	fd = open(path, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
	if (fd == -1) {
		tcmu_err("could not open %s: %m\n", path);
		return -1;
	}
	file_len = strlen(value);
	ssize_t ret = write(fd, value, file_len);
	close(fd);
	return ret;
}

// 分割后的IO请求结构。

struct io_segment
{
	char* key;
	char* value;
	off_t offset;
	size_t len;
};

/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    iqn        [iSCSI的标准命名符]
 * @in    offset     [IO请求中逻辑盘偏移地址]
 * @in    fragment_size [对象存储单个对象的大小]
 * @out   key  	     [生成的Key值]
 * @return   gen_key_state
 * 这个函数通过iqn和offset， 生成对应块的key值。
 */
BOOL gen_key(const char* iqn, off_t offset, size_t fragment_size, char* key)
{
	int ret = sprintf(key, "%s_%d", iqn, offset/fragment_size);
	if (ret < 0){
		tcmu_err("could not generate key; iqn:%s; offset:0x%x\n", iqn, offset);
		return false;
	}
	return true;
}

/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    dev        [TCMU的块设备结构]
 * @in    length     [读写IO的总长度]
 * @in    offset     [读写IO的逻辑地址]
 * @out    ios       [IO条带结构指针(本质上是一个一维数组)]
 * @return   ios_cnt    [IO条带结构的数量]
 * 该函数根据偏移、长度等读写信息来生成ios结构。每个ios存储一个条带的数据。
 */
int gen_ios(struct tcmu_device *dev, size_t length, off_t offset, 
	struct io_segment **p_ios)
{
	//获取iqn、条带大小fragment_size
	struct file_state *state = tcmur_dev_get_private(dev);
	//计算条带编号
	int s_count = offset / state->fragment_size;
	int e_count = (offset + length - 1) / state->fragment_size;
	//计算分割后的条带数量
	size_t ios_cnt = e_count - s_count + 1;
	struct io_segment *current_ios;
	//生成ios一维数组
	*p_ios = (struct io_segment *)malloc(ios_cnt * sizeof(struct io_segment));
	//遍历ios，分配key和value的内存空间，填充offset、len、key内容。
	for(i = s_count; i < e_count + 1  ; ++i){
		*current_ios = *p_ios+i-s_count;
		current_ios->offset = max(offset, i * state->fragment_size);
		current_ios->len = min(offset + length, (i+1) * state->fragment_size) - current_ios->offset;
		current_ios->key = (char *)malloc(1024 * sizeof(char));
		current_ios->value = (char *)malloc(state->fragment_size);
		gen_key(state->iqn, current_ios->offset, state->fragment_size, current_ios->key);
	}
	return ios_cnt;
}

/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    ios       [ios数组指针]
 * @in    ios_cnt   [ios数量]
 * 该函数用于释放ios结构占用的空间
 */
void free_ios(struct io_segment *ios, size_t ios_cnt){
	int i = 0;
	for(i = 0; i < ios_cnt; ++i){
		free((ios+i)->key);
		free((ios+i)->value);
	}
	free(io_segment);
}

/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    src        [内存指针，指向需要写入ios的数据]
 * @in    length     [数据长度]
 * @out   ios        [ios数组指针，其中的ios中的value成为输出，其他参数作为输入]
 * @in    ios_cnt    [ios数量]
 * @return           [description]
 * 该函数根据length和ios中的参数，将内存中的数据拷贝到ios数组的value空间中。
 */
size_t mem_2_ios(void* src, size_t length,
				struct io_segment *ios, size_t ios_cnt)
{
	size_t copied = 0;
	size_t to_copy = 0;
	while (length && ios_cnt) {
		to_copy = min(ios->len, length);
		if (to_copy) {
			memcpy(ios->value, src + copied, to_copy);
			length -= to_copy;
			copied += to_copy;
			ios->value += to_copy;
			ios->value -= to_copy;
		}
		ios++;
		ios_cnt--;
	}
	return copied;
}
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @in    dest       [内存指针，指向需要被写入的地址]
 * @in    length     [数据长度]
 * @out   ios        [ios数组指针，其中的ios中的value成为输出，其他参数作为输入]
 * @in    ios_cnt    [ios数量]
 * @return           [description]
 * 该函数根据length和ios中的参数，将内存ios数组的value空间中的数据拷贝到内存中。
 */
size_t ios_2_mem(char* dest, size_t length,
				struct io_segment *ios, size_t ios_cnt)
{
	size_t copied = 0;
	size_t to_copy = 0;
	while (length && ios_cnt) {
		to_copy = min(ios->len, length);
		if (to_copy) {
			memcpy(dest + copied, ios->value, to_copy);

			ios->len -= to_copy;
			copied += to_copy;
			ios->value += to_copy;
			ios->len -= to_copy;
		}
		ios++;
		ios_cnt--;
	}
	return copied;
}
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @param    ios        [ios结构]
 * @param    fragment_size [条带大小]
 * @return   ret        [写入数据的大小]
 * 该函数通过调用OBJ_read，完成将单个ios中的数据从KV数据库中读取出来的任务。
 * 根据key值读取出相关数据后，我们根据ios->offset截取需要的数据数据，保存在ios->value中。
 */
int seg_read(struct io_segment* ios, size_t fragment_size){
	char* value = (char*)malloc(fragment_size);
	int ret;
	memset(value, ' ', fragment_size);
	OBJ_read(ios->key, value);
	ret = memcpy(ios->value, value + ios->offset % fragment_size, ios->len);
	free(value);
	return ret;
}
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @param    ios        [ios结构]
 * @param    fragment_size [条带大小]
 * @return   ret        [写入数据的大小]
 * 该函数通过调用OBJ_read，完成将单个ios中的数据写入KV数据库的任务。
 * 先key值读取出相关对象，我们根据ios->offset和ios->value修改对象，再写回KV数据库中。
 */
int seg_write(struct io_segment* ios, size_t fragment_size){
	char* value = (char*)malloc(fragment_size);
	int ret;
	memset(value, ' ', fragment_size);
	OBJ_read(ios->key, value);
	ret = memcpy(value + ios->offset % fragment_size, ios->value, ios->len);
	OBJ_write(ios->key, value);
	free(value);
	return ret;
}
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @param    dev        [tcmu块设备结构指针]
 * @param    cmd        [iSCSI处理函数指针]
 * @param    iov        [iov数组指针，最终读取的数据需要写入该结构]
 * @param    iov_cnt    [iov个数]
 * @param    length     [待读取的数据长度]
 * @param    offset     [带读取的数据在块设备上的偏移]
 * @return   ret        [成功读取的数据大小]
 * 这个函数是自定义的read实现。为了完成块设备到对象设备的协议转换，总进行以下步骤：
 * 1.根据offset和length生成ios结构；
 * 2.从KV数据库读取数据到ios结构；
 * 3.完成ios->mem的转换；
 * 4.完成mem->iov的转换。
 */
static int file_read(struct tcmu_device *dev, struct tcmulib_cmd *cmd,
		      struct iovec *iov, size_t iov_cnt, size_t length,
		      off_t offset)
{
	struct file_state *state = tcmur_dev_get_private(dev);
	struct io_segment *ios;
	size_t ios_cnt = gen_ios(*dev, length, offset, &ios);
	ssize_t ret;
	char* buffer = (char*)malloc(length);

	for(i = 0; i < ios_cnt; ++i) {
		ret = seg_read(ios+i, state->fragment_size);
		if (ret < 0) {
			tcmu_err("read failed: %m\n");
			ret = TCMU_STS_WR_ERR;
			return ret;
		}
	}
	if (ret = ios_2_mem(ios, ios_cnt, buffer, length) < 0){
		tcmu_err("read failed: %m\n");
		ret = TCMU_STS_WR_ERR;
		return ret;
	}
	if (ret = tcmu_memcpy_into_iovec(iov, iov_cnt, buffer, length) < 0){
		tcmu_err("read failed: %m\n");
		ret = TCMU_STS_WR_ERR;
		return ret;
	}
	free(buffer);
	free_ios(ios, ios_cnt);
	ret = TCMU_STS_OK;
	return ret;
}
/**
 * @Author   Liar
 * @DateTime 2019-07-18
 * @param    dev        [tcmu块设备结构指针]
 * @param    cmd        [iSCSI处理函数指针]
 * @param    iov        [iov数组指针，最终读取的数据需要写入该结构]
 * @param    iov_cnt    [iov个数]
 * @param    length     [待读取的数据长度]
 * @param    offset     [带读取的数据在块设备上的偏移]
 * @return   ret        [成功读取的数据大小]
 * 这个函数是自定义的write实现。为了完成块设备到对象设备的协议转换，总进行以下步骤：
 * 1.根据offset和length生成ios结构；
 * 2.完成iov->mem的转换；
 * 3.完成mem->ios的转换；
 * 4.将ios中的数据最终写入的KV数据库中。
 */
static int file_write(struct tcmu_device *dev, struct tcmulib_cmd *cmd,
		      struct iovec *iov, size_t iov_cnt, size_t length,
		      off_t offset)
{
	struct file_state *state = tcmur_dev_get_private(dev);
	struct io_segment *ios;
	size_t ios_cnt = gen_ios(*dev, length, offset, &ios);
	ssize_t ret;
	char* buffer = (char*)malloc(length);
//  完成iov->mem的数据转换
	if (ret = tcmu_memcpy_from_iovec(iov, iov_cnt, buffer, length) < 0){
		tcmu_err("write failed: %m\n");
		ret = TCMU_STS_WR_ERR;
		return ret;
	}
//  完成mem->ios的数据转换
	if (ret = mem_2_ios(iov, iov_cnt, buffer, length) < 0){
		tcmu_err("write failed: %m\n");
		ret = TCMU_STS_WR_ERR;
		return ret;
	}
//  将ios结构中的数据通过seg_write函数写入后端存储。
	for(i = 0; i < ios_cnt; ++i) {
		ret = seg_write(ios+i, state->fragment_size);
		if (ret < 0) {
			tcmu_err("write failed: %m\n");
			ret = TCMU_STS_WR_ERR;
			return ret;
		}
	}
//回收mem空间和ios结构。
	free(buffer);
	free_ios(ios, ios_cnt);
	ret = TCMU_STS_OK;
	return ret;
}